

// #define _GNU_SOURCE
#include <dlfcn.h>

#include "Hook.h"
#include "fd_manager.h"
#include "IOScheduler.h"
#include "Coroutine.h"
#include "Timer.h"
#include <cstdarg>
#include <cstring>
#include <memory>
#include <cstdint>
#include <sys/ioctl.h>
#include <fcntl.h>


#define HOOK_FUN(f)\
    f(sleep) \
    f(usleep) \
    f(nanosleep) \
    f(socket) \
    f(connect) \
    f(accept) \
    f(read) \
    f(readv) \
    f(recv) \
    f(recvfrom) \
    f(recvmsg) \
    f(write) \
    f(writev) \
    f(send) \
    f(sendto) \
    f(sendmsg) \
    f(close) \
    f(fcntl) \
    f(ioctl) \
    f(getsockopt) \
    f(setsockopt)

namespace zepco {

static thread_local bool t_hook_enable = false;

bool is_hook_enable() {
    return t_hook_enable;
}

void set_hook_enable(bool flag) {
    t_hook_enable = flag;
}

void hook_init() {
    static bool is_inited = false;
    if (is_inited) {
        return;
    }

    is_inited = true;

#define HOOK_FUN_INER(name) name##_f = (name##_t)dlsym(RTLD_NEXT, #name);
    HOOK_FUN(HOOK_FUN_INER)
#undef HOOK_FUN_INER
}

struct HookIniter {
    HookIniter() {
        hook_init();
    }
};

static HookIniter s_hook_initer;

}

struct timer_info {
    int cancelled = 0;
};

template<typename OriginFun, typename ...Args>
static ssize_t do_io(int fd, OriginFun fun, const char *hook_fun_name, uint32_t event /* READ or WRITE */, int timeout_so /* SO_RCVTIMEO or SO_SNDTIMEO */, Args &&...args) {
    if (!zepco::t_hook_enable) {
        return fun(fd, std::forward<Args>(args)...);
    }

    std::shared_ptr<zepco::Fdctx> ctx = zepco::FdMgr::GetInstance()->get(fd);

    // 未在fdmanager中找到fd对应的fdctx执行原始调用
    if (!ctx)
        return fun(fd, std::forward<Args>(args)...);

    // fd已经关闭
    if (ctx->isClosed()) {
        errno = EBADF; // EBADF 表示fd无效或已经关闭
        return -1;
    }

    // 如果不是sockfd 或 用户设置非阻塞模式 调用原始函数
    if (!ctx->isSocket() || ctx->getUserNonBlock())
        return fun(fd, std::forward<Args>(args)...);

    // 获取超时设置 初始化time_info结构体 用于后续超时管理取消操作
    uint64_t timeout = ctx->getTimeout(timeout_so);
    std::shared_ptr<timer_info> tinfo(new timer_info);

retry:
    // 调用原始IO函数 如果由于系统中断EINTR 导致操作失败 函数重试
    ssize_t n = fun(fd, std::forward<Args>(args)...);
    while (n == -1 && errno == EINTR) {
        n = fun(fd, std::forward<Args>(args)...);
    }

    // 如果操作资源暂时不可用（EAGAIN） 添加事件监听器来等待资源可用 有超时设置会设置条件计时器来取消事件
    if (n == -1 && errno == EAGAIN) {
        zepco::IOManager *iom = zepco::IOManager::GetThis();
        std::shared_ptr<zepco::Timer> timer; // 唯一功能 --> 在io超时时取消事件
        std::weak_ptr<timer_info> winfo(tinfo); // 追踪timeinfo对象是否存在
        // 1. timeour has been set
        if (timeout != (uint64_t) -1) { // 检查是否设置超时事件
            timer = iom->addConditionTimer(timeout, [winfo, fd, iom, event]() {
                auto tmp = winfo.lock();
                if (!tmp || tmp->cancelled) { // 如果 timer_info 已经释放 或 操作已被取消 直接返回
                    return;
                }
                tmp->cancelled = ETIMEDOUT; // 达到超时时间 并且事件未被处理（cancelled == 0）
                // 取消该文件描述符上的事件 并触发回调 （恢复被挂起的协程）
                iom->cancelEvent(fd, (zepco::IOManager::Event)(event)); // TODO:
            }, winfo);
        }

        // 2. add event
        // 将fd和event（要监听的事件 READ or WRITE）添加到IOManager中管理
        int ret = iom->addEvent(fd, (zepco::IOManager::Event)(event));
        if (ret == -1) {
            if (timer) // ret == -1 addevent 失败 将之前设置的timer取消
                timer->cancel();
            return -1;
            std::cout /* cerr */ << hook_fun_name << " addEvent(" << fd << ", " << event << ")\n";
        } else {
            // 将自己挂起 等待事件触发唤醒
            zepco::Zephyr::GetThis()->yield();

            // 恢复后 取消timer计时器
            if (timer) {
                timer->cancel();
            }

            // 是否因超时而被取消 将errno设置为EITIMRDOUT 并返回-1表示操作失败
            if (tinfo->cancelled == ETIMEDOUT) {
                errno = tinfo->cancelled;
                return -1;
            }

            // 没有超时 重试这个操作
            goto retry;
        }
    }
    return n;
}


extern "C" {

#define HOOK_FUN_INER(name) name##_t name##_f = nullptr;
    HOOK_FUN(HOOK_FUN_INER)
#undef HOOK_FUN_INER

    unsigned int sleep(unsigned int seconds) {
        if (!zepco::t_hook_enable) {
            return sleep_f(seconds);
        }

        std::shared_ptr<zepco::Zephyr> zephyr = zepco::Zephyr::GetThis();
        zepco::IOManager *iom = zepco::IOManager::GetThis();

        iom->addTimer(seconds * 1000, [zephyr, iom] () {
            iom->scheduleLock(zephyr, -1);
        });
        zephyr->yield();
        return 0;
    }

    int usleep(useconds_t usec) {
        if (!zepco::t_hook_enable) {
            return usleep_f(usec);
        }

        std::shared_ptr<zepco::Zephyr> zephyr = zepco::Zephyr::GetThis();
        zepco::IOManager *iom = zepco::IOManager::GetThis();

        iom->addTimer(usec / 1000, [zephyr, iom] () {
            iom->scheduleLock(zephyr, -1);
        });
        zephyr->yield();
        return 0;
    }

    int nanosleep(const struct timespec *req, struct timespec *rem) {
        if (!zepco::t_hook_enable) {
            return nanosleep_f(req, rem);
        }

        int timeout_ms = req->tv_sec * 1000 + req->tv_nsec / 1000 / 1000;

        std::shared_ptr<zepco::Zephyr> zephyr = zepco::Zephyr::GetThis();
        zepco::IOManager *iom = zepco::IOManager::GetThis();

        iom->addTimer(timeout_ms, [zephyr, iom]() {
            iom->scheduleLock(zephyr, -1);
        });
        zephyr->yield();
        return 0;
    }


    int socket(int domain, int type, int protocol) noexcept {
        if (!zepco::t_hook_enable) {
            return socket_f(domain, type, protocol);
        }


        // 创建sockfd 并交由FdMgr管理
        int fd = socket_f(domain, type, protocol);
        if (fd == -1) {
            std::cerr << "socket() failed: " << strerror(errno) << std::endl;
            return fd;
        }

        zepco::FdMgr::GetInstance()->get(fd, true);
        return fd;
    }

    // 在连接超时的情况下处理非阻塞套接字的实现
    int connect_with_timeout(int fd, const struct sockaddr *addr, socklen_t addrlen, uint64_t timeout_ms) {
        if (!zepco::t_hook_enable) {
            return connect_f(fd, addr, addrlen);
        }

        std::shared_ptr<zepco::Fdctx> ctx = zepco::FdMgr::GetInstance()->get(fd);
        if (!ctx || ctx->isClosed()) {
            errno = EBADF;
            return -1;
        }

        if (!ctx->isSocket()) {
            return connect_f(fd, addr, addrlen);
        }

        if (ctx->getUserNonBlock()) {
            return connect_f(fd, addr, addrlen);
        }

        int n = connect_f(fd, addr, addrlen);
        if (n == 0) {
            return 0;
        } else if (n != -1 || errno == EINPROGRESS) {
            return n;
        }

        zepco::IOManager *iom = zepco::IOManager::GetThis();
        std::shared_ptr<zepco::Timer> timer;
        std::shared_ptr<timer_info> tinfo(new timer_info);
        std::weak_ptr<timer_info> winfo(tinfo);

        if (timeout_ms != (uint64_t) -1) {
            timer = iom->addConditionTimer(timeout_ms, [winfo, fd, iom]() {
                auto t = winfo.lock();
                if (!t || t->cancelled) {
                    return;
                }
                t->cancelled = ETIMEDOUT;
                iom->cancelEvent(fd, zepco::IOManager::WRITE);
            }, winfo);
        }

        int ret = iom->addEvent(fd, zepco::IOManager::WRITE);

        if (ret == -1) {
            if (timer) {
                timer->cancel();
            }
            std::cerr << "connect addEvent(" << fd << ", WRITE) error";
        } else {
            zepco::Zephyr::GetThis()->yield();
            if (timer) {
                timer->cancel();
            }

            if (tinfo->cancelled) {
                errno = tinfo->cancelled;
                return -1;
            }
        }

        int error = 0;
        socklen_t len = sizeof(int);
        if (-1 == getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len)) {
            return -1;
        }
        if (error == 0) {
            return 0;
        } else {
            errno = error;
            return -1;
        }
    }

    static uint64_t s_connect_timeout = -1;
    int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
        return connect_with_timeout(sockfd, addr, addrlen, s_connect_timeout);
    }

    int accept(int sockfd, struct sockaddr *addr, socklen_t *addr_len) {
        int fd = do_io(sockfd, accept_f, "accept", zepco::IOManager::READ, SO_RCVTIMEO, addr, addr_len);

        if (fd >= 0)
            zepco::FdMgr::GetInstance()->get(fd, true);

        return fd;
    }

    // read event
    ssize_t read(int fd, void *buf, size_t count) {
        return do_io(fd, read_f, "read", zepco::IOManager::READ, SO_RCVTIMEO, buf, count);
    }
    ssize_t readv(int fd, const struct iovec *iov, int iovcnt) {
        return do_io(fd, readv_f, "readv", zepco::IOManager::READ, SO_RCVTIMEO, iov, iovcnt);
    }

    ssize_t recv(int sockfd, void *buf, size_t len, int flags) {
        return do_io(sockfd, recv_f, "recv", zepco::IOManager::READ, SO_RCVTIMEO, buf, len, flags);
    }
    ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen) {
        return do_io(sockfd, recvfrom_f, "recvfrom", zepco::IOManager::READ, SO_RCVTIMEO, buf, len, flags, src_addr, addrlen);
    }
    ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags) {
        return do_io(sockfd, recvmsg_f, "recvmsg", zepco::IOManager::READ, SO_RCVTIMEO, msg, flags);
    }

    // write event
    ssize_t write(int fd, const void *buf, size_t count) {
        return do_io(fd, write_f, "write", zepco::IOManager::WRITE, SO_SNDTIMEO, buf, count);
    }
    ssize_t writev(int fd, const struct iovec *iov, int iovcnt) {
        return do_io(fd, writev_f, "writev", zepco::IOManager::WRITE, SO_SNDTIMEO, iov, iovcnt);
    }

    ssize_t send(int sockfd, const void *buf, size_t len, int flags) {
        return do_io(sockfd, send_f, "send", zepco::IOManager::WRITE, SO_SNDTIMEO, buf, len, flags);
    }
    ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) {
        return do_io(sockfd, sendto_f, "sendto", zepco::IOManager::WRITE, SO_SNDTIMEO, buf, len, flags, dest_addr, addrlen);
    }
    ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) {
        return do_io(sockfd, sendmsg_f, "sendmsg", zepco::IOManager::WRITE, SO_SNDTIMEO, msg, flags);
    }


    int close(int fd) {
        if (!zepco::t_hook_enable) {
            return close_f(fd);
        }

        std::shared_ptr<zepco::Fdctx> ctx = zepco::FdMgr::GetInstance()->get(fd);

        if (ctx) {
            auto iom = zepco::IOManager::GetThis();
            if (iom) {
                iom->cancelAll(fd);
            }

            zepco::FdMgr::GetInstance()->del(fd);
        }

        return close_f(fd);
    }

    int ioctl(int fd, unsigned long request, ...) noexcept {
        va_list va;
        va_start(va, request);
        void *arg = va_arg(va, void *);
        va_end(va);

        if (FIONBIO == request) {
            bool user_nonblock = !!*(int *)arg; // !! --> 保证转换为bool
            std::shared_ptr<zepco::Fdctx> ctx = zepco::FdMgr::GetInstance()->get(fd);
            if (!ctx || ctx->isClosed() || !ctx->isSocket()) {
                return ioctl_f(fd, request, arg);
            }

            ctx->setUserNonBlock(user_nonblock);
        }

        return ioctl_f(fd, request, arg);
    }


    int fcntl(int fd, int cmd, ...) {
        va_list va;
        va_start(va, cmd);

        switch (cmd) {
            case F_SETFL: { // 用于设置fd的状态标志 阻塞非阻塞
                int arg = va_arg(va, int);
                va_end(va);
                std::shared_ptr<zepco::Fdctx> ctx = zepco::FdMgr::GetInstance()->get(fd);
                if (!ctx || ctx->isClosed() || ctx->isSocket())
                    return fcntl_f(fd, cmd, arg);
                ctx->setUserNonBlock(arg & O_NONBLOCK);
                if (ctx->getSysNonBlock()) {
                    arg |= O_NONBLOCK;
                } else {
                    arg &= ~O_NONBLOCK;
                }
                return fcntl_f(fd, cmd, arg);
            }
            break;
            case F_GETFL: {
                va_end(va);
                int arg = fcntl(fd, cmd);
                std::shared_ptr<zepco::Fdctx> ctx = zepco::FdMgr::GetInstance()->get(fd);
                if (!ctx || ctx->isClosed() || ctx->isSocket())
                    return arg;


                
                // 展示给用户 显示用户指定的值
                // 底层根据系统设置决定
                if (ctx->getUserNonBlock())
                    return arg | O_NONBLOCK;
                else
                    return arg & ~O_NONBLOCK;
            }
            break;

            case F_DUPFD: 
            case F_DUPFD_CLOEXEC:
            case F_SETFD:
            case F_SETOWN:
            case F_SETSIG:
            case F_SETLEASE:
            case F_NOTIFY:
#ifdef F_SETPIPE_SZ
            case F_SETPIPE_SZ:
#endif
                {
                    int arg = va_arg(va, int);
                    va_end(va);
                    return fcntl_f(fd, cmd, arg);
                }
                break;

            case F_GETFD:
            case F_GETOWN:
            case F_GETSIG:
            case F_GETLEASE:
#ifdef F_GETPIPE_SZ
            case F_GETPIPE_SZ:
#endif
                {
                    va_end(va);
                    return fcntl_f(fd, cmd);
                }
                break;
            case F_SETLK:
            case F_SETLKW:
            case F_GETLK: {
                struct flock *arg = va_arg(va, struct flock *);
                va_end(va);
                fcntl_f(fd, cmd, arg);
            }
            break;
            case F_GETOWN_EX:
            case F_SETOWN_EX: {
                struct f_owner_exlock *arg = va_arg(va, struct f_owner_exlock *);
                va_end(va);
                fcntl_f(fd, cmd, arg);
            }
            break;
            default:
                va_end(va);
                return fcntl_f(fd, cmd);
        }
        
    }

    int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen) noexcept {
        return getsockopt_f(sockfd, level, optname, optval, optlen);
    }

    int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) noexcept {
        if (zepco::is_hook_enable()) {
            return setsockopt_f(sockfd, level, optname, optval, optlen);
        }


        if (level == SOL_SOCKET) {
            if (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO) {
                auto ctx = zepco::FdMgr::GetInstance()->get(sockfd);
                if (ctx) {
                    const timeval *v = (const timeval *)optval;
                    ctx->setTimeout(optname, v->tv_sec * 1000 + v->tv_usec / 1000);
                }
            }
        }

        return setsockopt_f(sockfd, level, optname, optval, optlen);
    }


}